package com.jintian.smart.kernel.flowable.component;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.jintian.smart.kernel.core.auth.IOperatorProvider;
import com.jintian.smart.kernel.core.flow.*;
import com.jintian.smart.kernel.flowable.api.PermissionService;
import com.jintian.smart.kernel.module.Model;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.URLBuilder;
import org.apache.dubbo.config.annotation.DubboService;
import org.beetl.core.Configuration;
import org.beetl.core.GroupTemplate;
import org.beetl.core.Template;
import org.beetl.core.resource.StringTemplateResourceLoader;
import org.beetl.sql.core.SQLManager;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;


@DubboService(timeout = 5000)
@Component
public class DefaultTaskOperation extends StringRender implements ITaskOperation<List<TaskInfo>>, BeanPostProcessor {
    private static final Map<FlowAction, ITaskAction<List<TaskInfo>>> actionMap = new HashMap<>();
    @Autowired
    private ProcessEngine processEngine;

    @Autowired
    private IOperatorProvider operatorProvider;

    @Autowired
    private SQLManager sqlManager;

    @Autowired
    private PermissionService permissionService;

    @Override
    public void registerTaskAction(FlowAction action, ITaskAction<List<TaskInfo>> taskAction) {
        if (action.isActionType(ActionType.task)) {
            actionMap.put(action, taskAction);
        }
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof ITaskAction) {
            ITaskAction taskAction = (ITaskAction) bean;
            this.registerTaskAction(taskAction.getAction(), taskAction);
        }
        return bean;
    }

    @Override
    public List<TaskInfo> complete(String taskId, ProcessControl processControl) throws Exception {
        return actionMap.get(FlowAction.complete).execute(taskId, processControl);
    }

    @Override
    public List<TaskInfo> fallback(String taskId, ProcessControl processControl) throws Exception {
        return actionMap.get(FlowAction.fallback).execute(taskId, processControl);
    }

    @Override
    public void claim(String taskId, ProcessControl processControl) throws Exception {
        actionMap.get(FlowAction.claim).execute(taskId, processControl);
    }

    @Override
    public void unclaim(String taskId, ProcessControl processControl) throws Exception {
        actionMap.get(FlowAction.unclaim).execute(taskId, processControl);
    }

    @Override
    public List<TaskInfo> transform(String taskId, ProcessControl processControl) throws Exception {
        return actionMap.get(FlowAction.transform).execute(taskId, processControl);
    }

    @Override
    public void withdraw(String taskId, ProcessControl processControl) throws Exception {
        actionMap.get(FlowAction.withdraw).execute(taskId, processControl);
    }

    @Override
    public void abort(String taskId, ProcessControl processControl) throws Exception {
        actionMap.get(FlowAction.abortTask).execute(taskId, processControl);
    }

    @Override
    public String formUrl(String taskId) throws Exception {
        Task task = processEngine.getTaskService().createTaskQuery().taskId(taskId).singleResult();
        ProcessInstance pi = null;
        String formKey = task.getFormKey();
        if (formKey != null && formKey.contains("${")) {
            pi = processEngine.getRuntimeService().createProcessInstanceQuery().includeProcessVariables()
                    .processInstanceId(task.getProcessInstanceId()).singleResult();
            String bizClass = (String) pi.getProcessVariables().get(FlowAction.VARNAME_START_BIZCLASS);
            String modelName = (String) pi.getProcessVariables().get(FlowAction.VARNAME_START_MODELNAME);
            Class<?> bizBeanClass = null;
            Object bizBean = null;
            if (bizClass != null) {
                if (modelName != null && this.applicationContext.getParent() != null) {
                    Model model = this.applicationContext.getBean("model$" + modelName, Model.class);
                    bizBeanClass = model.getModelContext().getClassLoader().loadClass(bizClass);
                } else {
                    bizBeanClass = this.applicationContext.getClassLoader().loadClass(bizClass);
                }
                bizBean = this.sqlManager.single(bizBeanClass, pi.getBusinessKey());
            }
            Map<String, Object> variants = new HashMap<>();
            variants.put(FlowAction.TRANSIENT_VARNAME_BIZBEAN, bizBean);
            variants.put(FlowAction.TRANSIENT_VARNAME_OPERATOR, operatorProvider.currentOperator());
            formKey = this.format(formKey, variants);
        }
        URL url = URLBuilder.valueOf(formKey);
        if (!url.hasParameter(ITaskOperation.FORMURL_PARAM_BUSINESS_KEY)) {
            if (pi == null) {
                pi = processEngine.getRuntimeService().createProcessInstanceQuery()
                        .processInstanceId(task.getProcessInstanceId()).singleResult();
            }
            url = url.addParameter(ITaskOperation.FORMURL_PARAM_BUSINESS_KEY, pi.getBusinessKey());
        }
        if (!url.hasParameter(ITaskOperation.FORMURL_PARAM_PROCESSINSTANCEID)) {
            url = url.addParameter(ITaskOperation.FORMURL_PARAM_PROCESSINSTANCEID, task.getProcessInstanceId());
        }
        if (!url.hasParameter(ITaskOperation.FORMURL_PARAM_TASKID)) {
            url = url.addParameter(ITaskOperation.FORMURL_PARAM_TASKID, task.getId());
        }
        if (!url.hasParameter(ITaskOperation.FORMURL_PARAM_PROCESS)) {
            url = url.addParameter(ITaskOperation.FORMURL_PARAM_PROCESS, pi.getProcessDefinitionKey());
        }
        if (!url.hasParameter(ITaskOperation.FORMURL_PARAM_ACTIVITY)) {
            url = url.addParameter(ITaskOperation.FORMURL_PARAM_ACTIVITY, task.getTaskDefinitionKey());
        }
        if (!url.hasParameter(ITaskOperation.FORMURL_PARAM_ACTIVITYLABEL)) {
            url.addParameter(ITaskOperation.FORMURL_PARAM_ACTIVITYLABEL, task.getName());
        }
        if (!permissionService.canExecute(FlowAction.getFlowOwner(), task)) {
            url = url.addParameter(ITaskOperation.FORMURL_PARAM_DO, "view");
        }
        return url.toString();
    }

    public static void main(String[] args) throws IOException {
        // 初始化代码
        StringTemplateResourceLoader resourceLoader = new StringTemplateResourceLoader();
        Configuration cfg = Configuration.defaultConfiguration();
        GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
        // 获取模板
        String expr = "<% \r\nvar a=".concat("[1,2,c+1 ]").concat(";\r\n%>").concat("\r\n /abc/${a}");
        Map<String, Object> variants = new HashMap<>();
        variants.put("c", "2");
        Template t = gt.getTemplate(expr);
        t.binding(variants);
        // 渲染结果
        String str = t.render();
        System.out.println(str);
    }

    @Override
    public List<TaskInfo> suspend(String taskId, ProcessControl processControl) throws Exception {
        return actionMap.get(FlowAction.suspend).execute(taskId, processControl);
    }

    @Override
    public List<TaskInfo> resume(String taskId, ProcessControl processControl) throws Exception {
        return actionMap.get(FlowAction.resume).execute(taskId, processControl);
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    @Override
    public ProcessControl queryProcessControl(String taskId, String action, Map<String, Object> transientVariables) throws Exception {
        ITaskAction ITaskAction = actionMap.get(FlowAction.valueOf(action));
        return ITaskAction.query(taskId, transientVariables);
    }
}
